package com.hup.utils.swing.customView;

import com.hup.utils.commons.start.LookThemeUtil;
import lombok.Getter;
import lombok.extern.log4j.Log4j2;

import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;

/**
 * 根据parent宽度,自动设定行列数的控件;
 * tips:在 {@link GridBagLayout}下,需要设置 {@link GridBagConstraints#fill} = {@link GridBagConstraints#BOTH}
 * 和{@link GridBagConstraints#weighty},才会正常展示控件
 * @author hugan
 * @date 2020/3/23
 */
@Log4j2
public class LiveGridView<T extends Component> extends JScrollPane {

    private JPanel panel;

    /**
     * 当前的竖直滚动距离;(已加入缩放比例)
     */
    @Getter
    private int verticalScrollIncrement;

    public LiveGridView() {
        setupUI();
        verticalScrollIncrement = getVerticalScrollBar().getUnitIncrement();
    }

    private void setupUI() {
        panel = new JPanel();
        panel.setLayout(new GridLayout(1, 1));
        //让子控件右到左排列
        //panel.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
        setViewportView(panel);
    }

    /**
     * @return 3个:{@link JViewport}, {@link ScrollBar}
     * @deprecated 使用 {@link #getAllChild()}获取业务需要的child
     */
    @Override
    public Component[] getComponents() {
        return super.getComponents();
    }

    @Override
    public void setBounds(int x, int y, int width, int height) {
        super.setBounds(x, y, width, height);
        log.debug("x={}, y={}, width={}, height={}", x, y, width, height);
        afterSizeChanged(width);
    }

    private void afterSizeChanged(int width) {
        int childCount = panel.getComponentCount();
        GridLayout layout = (GridLayout) panel.getLayout();
        if (childCount < 2 || width == 0) { //0, 1
            //不能直接return,需要处理:child从n减少到[0,1]的情况
            if (layout.getColumns() != 1 || layout.getRows() != 1) {
                layout.setColumns(1);
                layout.setRows(1);
                log.debug("set(1,1)");
            }
            return;
        }

        /*
        实现'永远不出现横向滚动条'
        核心逻辑:直接缩减总宽度,来计算所需要的行列数
        sbWidth是竖直滚动条的宽度
            它在展示在ui第二次调用本方法时才不是0,所以这里直接固定了最小值,且处理了缩放问题
            就是因为它占据了width,才会导致横向滚动条偶尔出现,
         */
        int sbWidth = (int) (Math.max(getVerticalScrollBar().getSize().width, 25) * LookThemeUtil.getScreenScale());
        width = width - sbWidth;
        //GridLayout会让每个child保持一样的大小,所以这里只取第一个child
        int preferredChildWid = panel.getComponent(0).getPreferredSize().width;
        //这里的行列数,不一定是实际layout的展示行列! 是'child再增多,达到最紧凑'的情况下的行列数
        int preferredCol = width / preferredChildWid;
        if (preferredCol == 0) preferredCol = 1;//parent宽度小于child宽度

        int preferredRow = childCount / preferredCol;
        if (childCount % preferredCol != 0) {
            preferredRow++;
        }
        log.debug("preferredChildWid={}, preferredCol={}, preferredRow={}", preferredChildWid, preferredCol, preferredRow);

        if (layout.getColumns() != preferredCol || layout.getRows() != preferredRow) {
            log.debug("preferredCol={}, preferredRow={}", preferredCol, preferredRow);
            /*
            这里虽然指定了行列数,
            但GridLayout还是会根据实际情况进行布局的
                如不会出现(两行3+1)的情况,而是(2+2)
             */
            layout.setColumns(preferredCol);
            layout.setRows(preferredRow);
        }
    }

    @SuppressWarnings("unchecked")
    public ArrayList<T> getAllChild() {
        ArrayList<T> res = new ArrayList<>();
        for (Component c : panel.getComponents()) res.add((T) c);
        return res;
    }

    /**
     * 添加子控件;原有的JScrollPane默认构造的情况下,是没有调用{@link JScrollPane#add(Component)};
     * 所以可以重写,实现添加子控件的功能;
     * @apiNote 需要调用 {@link #notifyChildChanged()} 通知刷新布局
     */
    @SuppressWarnings("unchecked")
    @Override
    public Component add(Component comp) {
        T c = (T) comp;//控制child类型
        panel.add(c);
        return comp;
    }

    /**
     * @apiNote 需要调用 {@link #notifyChildChanged()} 通知刷新布局
     */
    public void addAll(List<T> items) {
        for (Component item : items) panel.add(item);
    }

    /**
     * @apiNote 需要调用 {@link #notifyChildChanged()} 通知刷新布局
     */
    public ArrayList<T> removeAllChild() {
        ArrayList<T> res = getAllChild();
        panel.removeAll();
        return res;
    }

    /**
     * @apiNote 需要调用 {@link #notifyChildChanged()} 通知刷新布局
     */
    public void removeChild(T child) {
        panel.remove(child);
    }

    /**
     * 当前组件的child(批量)改变后,需要调用此方法刷新界面
     */
    public void notifyChildChanged() {
        afterSizeChanged(getWidth());
        //已经布置容器后，在修改此容器的子组件的时候（在容器中添加或移除组件，或者更改与布局相关的信息），应该调用上述方法
        validate();
        //需要重绘,否则界面还是不会更新的
        repaint();
    }

    public int indexOf(T child) {
        return getAllChild().indexOf(child);
    }

    /**
     * 设置竖直滚动距离,会处理缩放问题
     * @param val 单位是像素?不确定
     */
    public void setVerticalScrollIncrement(int val) {
        val = (int) (LookThemeUtil.getScreenScale() * val);
        verticalScrollIncrement = val;
        getVerticalScrollBar().setUnitIncrement(val);
    }

}
